C.S.Burner 🪙 ━►🔥GIT Node
Node / reticulum_mirror / MeshChatX / files / meshchatx / src / frontend / components / tools / ReticulumConfigEditorPage.vue
Displaying Raw • Download
meshchatx/src/frontend/components/tools/ReticulumConfigEditorPage.vue 65ca9ddc823fcc2c8f8653c553b944b85db0875f (65ca9ddc) Text, 11.67 KB
T8b949e<!-- SPDX-License-Identifier: 0BSD -->
Tff7b72<templateTff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-col flex-1 overflow-hidden min-w-0 bg-slate-50 dark:bg-zinc-950Ta5d6ff"Tff7b72>
Tff7b72<ToolsPageHeader
Te6edf3icon=Ta5d6ff"Ta5d6fffile-cogTa5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6fftools.reticulum_config_editor.titleTa5d6ff'Ta5d6ff)Ta5d6ff"
Te6edf3:description=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6fftools.reticulum_config_editor.descriptionTa5d6ff'Ta5d6ff)Ta5d6ff"
Te6edf3accent=Ta5d6ff"Ta5d6ffblueTa5d6ff"
Tff7b72>
Tff7b72<template #actionsTff7b72>
Tff7b72<button Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsecondary-chip py-1! px-3!Ta5d6ff" Te6edf3:disabled=Ta5d6ff"Ta5d6ffloadingTa5d6ff" @Te6edf3click=Ta5d6ff"Ta5d6ffloadConfigTa5d6ff"Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffrefreshTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-3.5 h-3.5Ta5d6ff" Tff7b72/>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6ffhidden sm:inlineTa5d6ff"Tff7b72>{{ Te6edf3$t("tools.reticulum_config_editor.reload") }}Tff7b72</span>
Tff7b72</button>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffsecondary-chip py-1! px-3! text-red-500! hover:bg-red-50! dark:hover:bg-red-900/20!Ta5d6ff"
Te6edf3:disabled=Ta5d6ff"Ta5d6ffloading || resettingTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffrestoreDefaultsTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffrestoreTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-3.5 h-3.5Ta5d6ff" Tff7b72/>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6ffhidden sm:inlineTa5d6ff"Tff7b72>{{ Te6edf3$t("tools.reticulum_config_editor.restore_defaults") }}Tff7b72</span>
Tff7b72</button>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffsecondary-chip py-1! px-3!Ta5d6ff"
Te6edf3:disabled=Ta5d6ff"Ta5d6ff!isDirty || savingTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffdiscardChangesTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffundoTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-3.5 h-3.5Ta5d6ff" Tff7b72/>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6ffhidden sm:inlineTa5d6ff"Tff7b72>{{ Te6edf3$t("tools.reticulum_config_editor.discard") }}Tff7b72</span>
Tff7b72</button>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffprimary-chip py-1! px-3!Ta5d6ff"
Te6edf3:disabled=Ta5d6ff"Ta5d6ff!isDirty || savingTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffsaveConfigTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffcontent-saveTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-3.5 h-3.5Ta5d6ff" Tff7b72/>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6ffhidden sm:inlineTa5d6ff"Tff7b72>{{
saving ? Te6edf3$t("tools.reticulum_config_editor.saving") : Te6edf3$t("tools.reticulum_config_editor.save")
}}Tff7b72</span>
Tff7b72</button>
Tff7b72</template>
Tff7b72</ToolsPageHeader>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffflex-1 overflow-y-auto overflow-x-hidden w-full px-3 sm:px-5 py-4 pb-[max(1rem,env(safe-area-inset-bottom))]Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-4 w-full min-w-0 max-w-6xl mx-autoTa5d6ff"Tff7b72>
Tff7b72<p
Te6edf3v-if=Ta5d6ff"Ta5d6ffconfigPathTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-gray-500 dark:text-gray-400 font-mono truncateTa5d6ff"
Te6edf3:title=Ta5d6ff"Ta5d6ffconfigPathTa5d6ff"
Tff7b72>
{{ configPath }}
Tff7b72</p>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffshowRestartReminderTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffbg-amber-600 text-white border border-amber-500/30 p-4 sm:rounded-xl flex flex-wrap gap-3 items-centerTa5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-3Ta5d6ff"Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffalertTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-6 h-6Ta5d6ff" Tff7b72/>
Tff7b72<divTff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-lg font-semiboldTa5d6ff"Tff7b72>
{{ Te6edf3$t("tools.reticulum_config_editor.restart_required") }}
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-smTa5d6ff"Tff7b72>
{{ Te6edf3$t("tools.reticulum_config_editor.restart_description") }}
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffml-auto inline-flex items-center gap-2 rounded-full bg-white px-4 py-1.5 text-sm font-bold text-amber-600 hover:bg-white/90 transition shadow-xs disabled:opacity-50Ta5d6ff"
Te6edf3:disabled=Ta5d6ff"Ta5d6ffreloadingRnsTa5d6ff"
Te6edf3:class=Ta5d6ff"Ta5d6ffreloadingRns ? Ta5d6ff'Ta5d6ff'Ta5d6ff : Ta5d6ff'Ta5d6ffanimate-pulse motion-reduce:animate-noneTa5d6ff'Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffreloadRnsTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffrestartTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-4 h-4Ta5d6ff" Tff7b72/>
{{ reloadingRns ? Te6edf3$t("app.reloading_rns") : Te6edf3$t("tools.reticulum_config_editor.restart_now") }}
Tff7b72</button>
Tff7b72</div>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffrounded-xl border border-gray-200 dark:border-zinc-800 bg-white dark:bg-zinc-900 overflow-hiddenTa5d6ff"
Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-wrap items-center justify-between gap-2 px-3 py-2 border-b border-gray-200 dark:border-zinc-800 bg-gray-50 dark:bg-zinc-900/60 text-xs text-gray-600 dark:text-gray-300Ta5d6ff"
Tff7b72>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-1.5Ta5d6ff"Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffinformation-outlineTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-3.5 h-3.5Ta5d6ff" Tff7b72/>
{{ Te6edf3$t("tools.reticulum_config_editor.info") }}
Tff7b72</span>
Tff7b72<span Te6edf3v-if=Ta5d6ff"Ta5d6ffisDirtyTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6fftext-amber-600 dark:text-amber-400 font-semiboldTa5d6ff"Tff7b72>
{{ Te6edf3$t("tools.reticulum_config_editor.unsaved") }}
Tff7b72</span>
Tff7b72</div>
Tff7b72<textarea
Te6edf3ref=Ta5d6ff"Ta5d6ffeditorRefTa5d6ff"
Te6edf3v-model=Ta5d6ff"Ta5d6ffcontentTa5d6ff"
Te6edf3spellcheck=Ta5d6ff"Ta5d6fffalseTa5d6ff"
Te6edf3autocapitalize=Ta5d6ff"Ta5d6ffoffTa5d6ff"
Te6edf3autocomplete=Ta5d6ff"Ta5d6ffoffTa5d6ff"
Te6edf3autocorrect=Ta5d6ff"Ta5d6ffoffTa5d6ff"
Te6edf3:placeholder=Ta5d6ff"Ta5d6ffloading ? Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6fftools.reticulum_config_editor.loadingTa5d6ff'Ta5d6ff) : Ta5d6ff'Ta5d6ff'Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffw-full bg-white dark:bg-zinc-900 text-gray-900 dark:text-white p-4 font-mono text-xs sm:text-sm resize-none focus:outline-hidden min-h-[420px] sm:min-h-[60vh]Ta5d6ff"
@keydown.tab.Te6edf3prevent=Ta5d6ff"Ta5d6ffinsertTabTa5d6ff"
Tff7b72>Tff7b72</textarea>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</template>
Tff7b72<scriptTff7b72>
import MaterialDesignIcon from "../MaterialDesignIcon.vue";
import ToastUtils from "../../js/ToastUtils";
import DialogUtils from "../../js/DialogUtils";
import GlobalState from "../../js/GlobalState";
import ToolsPageHeader from "./ToolsPageHeader.vue";
export default {
name: "ReticulumConfigEditorPage",
components: {
MaterialDesignIcon,
ToolsPageHeader,
},
beforeRouteLeave(to, from, next) {
if (!this.isDirty) {
next();
return;
}
DialogUtils.confirm(this.$t("tools.reticulum_config_editor.confirm_leave")).then((ok) => {
next(!!ok);
});
},
data() {
return {
content: "",
originalContent: "",
configPath: "",
loading: false,
saving: false,
resetting: false,
reloadingRns: false,
hasSavedChanges: false,
};
},
computed: {
isDirty() {
return this.content !== this.originalContent;
},
showRestartReminder() {
return this.hasSavedChanges || GlobalState.hasPendingInterfaceChanges;
},
},
async mounted() {
await this.loadConfig();
},
methods: {
async loadConfig() {
if (this.loading) return;
try {
this.loading = true;
const response = await window.api.get("/api/v1/reticulum/config/raw");
this.content = response.data.content || "";
this.originalContent = this.content;
this.configPath = response.data.path || "";
} catch (e) {
ToastUtils.error(e.response?.data?.error || this.$t("tools.reticulum_config_editor.failed_load"));
} finally {
this.loading = false;
}
},
async saveConfig() {
if (this.saving || !this.isDirty) return;
try {
this.saving = true;
ToastUtils.loading(this.$t("tools.reticulum_config_editor.saving"), 0, "rns-config-save");
const response = await window.api.put("/api/v1/reticulum/config/raw", {
content: this.content,
});
this.originalContent = this.content;
this.configPath = response.data.path || this.configPath;
this.hasSavedChanges = true;
GlobalState.hasPendingInterfaceChanges = true;
ToastUtils.success(response.data.message || this.$t("tools.reticulum_config_editor.saved"));
} catch (e) {
ToastUtils.error(e.response?.data?.error || this.$t("tools.reticulum_config_editor.failed_save"));
} finally {
ToastUtils.dismiss("rns-config-save");
this.saving = false;
}
},
async restoreDefaults() {
if (this.resetting) return;
const confirmed = await DialogUtils.confirm(this.$t("tools.reticulum_config_editor.confirm_restore"));
if (!confirmed) return;
try {
this.resetting = true;
ToastUtils.loading(this.$t("tools.reticulum_config_editor.restoring"), 0, "rns-config-restore");
const response = await window.api.post("/api/v1/reticulum/config/reset");
this.content = response.data.content || "";
this.originalContent = this.content;
this.configPath = response.data.path || this.configPath;
this.hasSavedChanges = true;
GlobalState.hasPendingInterfaceChanges = true;
ToastUtils.success(response.data.message || this.$t("tools.reticulum_config_editor.restored"));
} catch (e) {
ToastUtils.error(e.response?.data?.error || this.$t("tools.reticulum_config_editor.failed_restore"));
} finally {
ToastUtils.dismiss("rns-config-restore");
this.resetting = false;
}
},
discardChanges() {
if (!this.isDirty) return;
this.content = this.originalContent;
},
async reloadRns() {
if (this.reloadingRns) return;
try {
this.reloadingRns = true;
ToastUtils.loading(this.$t("app.reloading_rns"), 0, "rns-config-reload");
const response = await window.api.post("/api/v1/reticulum/reload");
ToastUtils.success(response.data.message || this.$t("tools.reticulum_config_editor.restart_done"));
this.hasSavedChanges = false;
GlobalState.hasPendingInterfaceChanges = false;
if (GlobalState.modifiedInterfaceNames?.clear) {
GlobalState.modifiedInterfaceNames.clear();
}
await this.loadConfig();
} catch (e) {
ToastUtils.error(e.response?.data?.error || this.$t("tools.reticulum_config_editor.failed_restart"));
} finally {
ToastUtils.dismiss("rns-config-reload");
this.reloadingRns = false;
}
},
insertTab(event) {
const target = event.target;
const start = target.selectionStart;
const end = target.selectionEnd;
const before = this.content.substring(0, start);
const after = this.content.substring(end);
this.content = `${before} ${after}`;
this.$nextTick(() => {
target.selectionStart = target.selectionEnd = start + 2;
});
},
},
};
Tff7b72</script>
Served by rngit 1.5.2 - Generated in 0.03s